/*
 * Copyright (C) 2024 Huawei Device Co., Ltd.
 * Licensed under the Apache License, Version 2.0 (the 'License');
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an 'AS IS' BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
#include "onload_callback_napi.h"
#include "imageknife_data_napi.h"
#include "napi_parser.h"
#include "napi_utils.h"

namespace ImageKnifePro {

OnLoadCallBackNapi::OnLoadCallBackNapi(napi_env env, napi_value value)
{
    env_ = env;
    napi_create_reference(env, value, 1, &jsThis_);

    napi_value jsFunc = NapiParser::ParseProperty(env, value, "onLoadStart");
    if (jsFunc != nullptr) {
        napi_create_reference(env, jsFunc, 1, &onLoadStart_);
        onLoadStart = [this](ImageInfo imageInfo) {
            this->CallJsOnLoadStart(imageInfo);
        };
    }

    jsFunc = NapiParser::ParseProperty(env, value, "onLoadSuccess");
    if (jsFunc != nullptr) {
        napi_create_reference(env, jsFunc, 1, &onLoadSuccess_);
        onLoadSuccess = [this](ImageInfo imageInfo) {
            this->CallJsOnLoadSuccess(imageInfo);
        };
    }

    jsFunc = NapiParser::ParseProperty(env, value, "onLoadFailed");
    if (jsFunc != nullptr) {
        napi_create_reference(env, jsFunc, 1, &onLoadFailed_);
        onLoadFailed = [this](std::string err, ImageInfo imageInfo) {
            this->CallJsOnLoadFailed(err, imageInfo);
        };
    }

    jsFunc = NapiParser::ParseProperty(env, value, "onLoadCancel");
    if (jsFunc != nullptr) {
        napi_create_reference(env, jsFunc, 1, &onLoadCancel_);
        onLoadCancel = [this](std::string reason, ImageInfo imageInfo) {
            this->CallJsOnLoadCancel(reason, imageInfo);
        };
    }
}

void OnLoadCallBackNapi::CallJsOnLoadStart(ImageInfo &imageInfo)
{
    napi_handle_scope scope;
    napi_open_handle_scope(env_, &scope);

    napi_status status;
    napi_value thisValue;
    napi_value jsFunc;
    napi_get_reference_value(env_, jsThis_, &thisValue);
    napi_get_reference_value(env_, onLoadStart_, &jsFunc);

    // 获取 request 中的 imageKnifeData 并转换为 JS 对象
    napi_value jsImageKnifeData = ImageKnifeDataNapi::ConvertToJSObject(env_, imageInfo);
    status = napi_call_function(env_, thisValue, jsFunc, 1, &jsImageKnifeData, nullptr);
    IMAGE_KNIFE_CHECK(status != napi_ok, ("call js onLoadStart failed, napi status:" + std::to_string(status)).c_str());
    napi_close_handle_scope(env_, scope);
}

void OnLoadCallBackNapi::CallJsOnLoadSuccess(ImageInfo &imageInfo)
{
    napi_handle_scope scope;
    napi_open_handle_scope(env_, &scope);
    // 调用 ConvertToJSObject 将 ImageKnifeData 转换为 JS 对象
    napi_value jsImageInfo = ImageKnifeDataNapi::ConvertToJSObject(env_, imageInfo);

    napi_status status;
    napi_value thisValue;
    napi_value jsFunc;

    napi_get_reference_value(env_, jsThis_, &thisValue);
    napi_get_reference_value(env_, onLoadSuccess_, &jsFunc);

    // 调用 JavaScript 回调函数
    status = napi_call_function(env_, thisValue, jsFunc, 1, &jsImageInfo, nullptr);
    if (status != napi_ok) {
        IMAGE_KNIFE_LOG(LOG_ERROR, "call js onLoadSuccess failed, napi status: %{public}d", status);
    }
    napi_close_handle_scope(env_, scope);
}

void OnLoadCallBackNapi::CallJsOnLoadFailed(std::string err, ImageInfo &imageInfo)
{
    napi_handle_scope scope;
    napi_open_handle_scope(env_, &scope);
    const int argc = 2;
    napi_value args[2];
    napi_create_string_utf8(env_, err.c_str(), NAPI_AUTO_LENGTH, &args[0]);
    args[1] = ImageKnifeDataNapi::ConvertToJSObject(env_, imageInfo);

    napi_status status;
    napi_value thisValue;
    napi_value jsFunc;
    napi_get_reference_value(env_, jsThis_, &thisValue);
    napi_get_reference_value(env_, onLoadFailed_, &jsFunc);

    status = napi_call_function(env_, thisValue, jsFunc, argc, args, nullptr);
    if (status != napi_ok) {
        IMAGE_KNIFE_LOG(LOG_ERROR, "call js onLoadFailed failed, napi status: %{public}d", status);
    }
    napi_close_handle_scope(env_, scope);
}

void OnLoadCallBackNapi::CallJsOnLoadCancel(std::string reason, ImageInfo &imageInfo)
{
    napi_handle_scope scope;
    napi_open_handle_scope(env_, &scope);
    const int argc = 2;
    napi_value args[2];
    napi_create_string_utf8(env_, reason.c_str(), NAPI_AUTO_LENGTH, &args[0]);
    args[1] = ImageKnifeDataNapi::ConvertToJSObject(env_, imageInfo);

    napi_status status;
    napi_value thisValue;
    napi_value jsFunc;
    napi_get_reference_value(env_, jsThis_, &thisValue);
    napi_get_reference_value(env_, onLoadCancel_, &jsFunc);

    status = napi_call_function(env_, thisValue, jsFunc, argc, args, nullptr);
    if (status != napi_ok) {
        IMAGE_KNIFE_LOG(LOG_ERROR, "call js onLoadCancel failed, napi status: %{public}d", status);
    }
    napi_close_handle_scope(env_, scope);
}

OnLoadCallBackNapi::~OnLoadCallBackNapi()
{
    NapiUtils::DeleteReferences(env_, {jsThis_, onLoadStart_, onLoadSuccess_, onLoadFailed_, onLoadCancel_});
}

}
